<?php
/*
 * @Author: your name
 * @Date: 2020-12-11 10:24:19
 * @LastEditTime: 2021-01-19 14:59:00
 * @LastEditors: Please set LastEditors
 * @Description: 规则下载逻辑
 * @FilePath: /special/application/api/service/netfilter/FirewallRuleDownloadService.php
 */
namespace app\api\service\netfilter;

use app\adminapiv3\service\module_manage\netfilter\SortRuleService;
use app\common\definition\HostDefinition;
use app\common\definition\netfilter\FirewallRuleDefinition;
use app\common\definition\netfilter\LearnDataDefinition;
use app\common\definition\netfilter\NetfilterConfigDefinition;
use app\common\exception\CustomException;
use think\Db;

class FirewallRuleDownloadService extends FirewallRuleService
{
    /**
     * @name: 规则下载
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-11-17 15:51:00
     */
    public function download ()
    {
        $learnConfig = Db::name('host_learn_config')->field('learn_status,danger_port,controlled_firewall,log_status,current_rule_group_status')->where('host_id', $this->host_id)->find();

        if (empty($learnConfig))
        {
            throw new CustomException('该主机未开启模块', 422);
        }
        

        if($learnConfig['current_rule_group_status'] != FirewallRuleDefinition::NORMAL_RULE_GROUP_STATUS)
        {
            return [
                'next_request_time' => 15
            ];
        }

        //当主机未接管防火墙时 不下发规则
        if($learnConfig['controlled_firewall'] === NetfilterConfigDefinition::FIREWALL_NOT_CONTROLLED)
        {
            return [
                'next_request_time' => 0,
                'create_time' => $this->nowtime,
                'type' => FirewallRuleDefinition::UNKONW_RULE_GROUP,
                'list' => [],
            ];
        }

        //如果主机已接管防火墙并处于学习状态 返回关闭危险端口规则
        if ($learnConfig['learn_status'] === LearnDataDefinition::START_STUDY)
        {
            $return = $this->getStudyRule($learnConfig['danger_port']);
            $return['next_request_time'] = 0;
            return $return;
        }


        //获取正常规则
        $return = $this->getNormalRule();
        $return['next_request_time'] = 0;
        
        return $return;
    }

    /**
     * @name: 更新规则预览规则日志
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-02 10:30:08
     */
    private function modifyPreviewContent ($rule)
    {
        $log_id = Db::name('netfilter_firewall_rule_log')->where('status', FirewallRuleDefinition::WATTING_FOR_ARCHIVE_LOG_STATUS)->where('type', FirewallRuleDefinition::NORMAL_RULE_GROUP)->where('host_id', $this->host_id)->value('id');
        $firewall_rule_id_a = array_column($rule, 'id');
        Db::startTrans();
        try
        {
            if ($log_id)
            {
                Db::name('netfilter_firewall_rule_log')->where('id', $log_id)->update(
                    [
                        'create_time' => $this->nowtime,
                        'signature_code' => md5($this->host_id . $this->nowtime),
                        'preview_content' => json_encode($rule)
                    ]
                );
            }
            else
            {
                $insertData = [
                    'host_id' => $this->host_id,
                    'create_time' => $this->nowtime,
                    'signature_code' => md5($this->host_id . $this->nowtime),
                    'error_code' => 0,
                    'error_message' => '',
                    'preview_content' => json_encode($rule),
                    'type' => FirewallRuleDefinition::NORMAL_RULE_GROUP,
                    'status' => FirewallRuleDefinition::WATTING_FOR_ARCHIVE_LOG_STATUS
                ];
                
                Db::name('netfilter_firewall_rule_log')->insert($insertData);
            }

            Db::name('netfilter_firewall_rule')->where('host_id', $this->host_id)->whereIn('id', $firewall_rule_id_a)->update(['status' => FirewallRuleDefinition::RULE_ENABLE]);
            Db::commit();
        } catch (\think\Exception $e) {
            Db::rollback();
            throw new CustomException('修改数据库失败:' . $e->getMessage(), 302);
        }
        
    }

    /**
     * @name: 学习规则
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-02 14:21:37
     */
    private function getStudyRule ($danger_port)
    {
        $return['create_time'] = $this->nowtime;
        $return['type'] = FirewallRuleDefinition::STUDY_RULE_GROUP;
        $danger_port = explode(',', $danger_port);
        $return['list'] = [];
        if($this->host_os == HostDefinition::LINUX_OPERATION_SYSTEM)
        {
            //关闭危险端口
            foreach ($danger_port as $port)
            {
                if(!empty($port))
                {
                    $return['list'][] = "-A INPUT -p tcp -m multiport  --dports $port -j DROP";
                }
            }
            //允许所有入站
            $return['list'][] = '-A INPUT -j ACCEPT';
        }
        elseif ($this->host_os == HostDefinition::WINDOWS_OPERATION_SYSTEM)
        {
            //允许所有
            $return['list'][] = 'netsh advfirewall set allprofiles state on';
            //允许所有
            $return['list'][] = 'netsh advfirewall set allprofiles firewallpolicy allowinbound,allowoutbound';
            //关闭危险端口
            foreach ($danger_port as $port)
            {
                if(!empty($port))
                {
                    $return['list'][] = 'netsh advfirewall firewall add rule name="study_rule" protocol=TCP dir=in localport=' . $port . ' action=block';
                }
            }
        }
        else
        {
            throw new CustomException('不支持该主机系统', 422);
        }
        
        return $return;
    }

    /**
     * @name: 获取正常规则
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2020-12-02 14:28:31
     */
    private function getNormalRule ()
    {
        //返回预览规则
        $return['create_time'] = $this->nowtime;
        $return['type'] = FirewallRuleDefinition::NORMAL_RULE_GROUP;

        $data = Db::name('netfilter_firewall_rule')->where('host_id', $this->host_id)->order('rank', 'ASC')->select();
        $SortRuleService = new SortRuleService();
        $data = $SortRuleService->addLogRule($this->host_id, $data);
        $rule_a = $SortRuleService->addDisguisePortRule($this->host_id, $this->host_os, $data);
        if($this->host_os == HostDefinition::LINUX_OPERATION_SYSTEM)
        {
            $rule_a = $SortRuleService->sortRule($rule_a);
        }
        
        $this->modifyPreviewContent($rule_a);

        $return['list'] = array_column($rule_a, 'content');

        if ($this->host_os == HostDefinition::WINDOWS_OPERATION_SYSTEM)
        {
            $firewall_config = Db::name('netfilter_firewall_config_windows')->where('host_id', $this->host_id)->find();
            $return['list'] = array_merge($return['list'], $this->addWindowsFirewallDefaultAction('private', $firewall_config));
            $return['list'] = array_merge($return['list'], $this->addWindowsFirewallDefaultAction('public', $firewall_config));
            $return['list'] = array_merge($return['list'], $this->addWindowsFirewallDefaultAction('domain', $firewall_config));
            $return['list'] = array_unique($return['list']);
        }

        return $return;
    }

    /**
     * @name: windows防火墙默认行为
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2021-01-19 13:05:27
     */
    private function addWindowsFirewallDefaultAction (string $profile, array $config)
    {
        $main_switch_s = $config[$profile . '_main_switch'] ? 'on' : 'off';
        $input_switch_s = $config[$profile . '_input'] ? 'allow' : 'block';
        $output_switch_s = $config[$profile . '_output'] ? 'allow' : 'block';
        
        $return = [
            sprintf('netsh advfirewall set %sprofile state %s', $profile, $main_switch_s),
            sprintf('netsh advfirewall set %sprofile firewallpolicy %sinbound,%soutbound', $profile, $input_switch_s, $output_switch_s)
        ];

        return $return;
    }
}